﻿using Management.Auxiliary;
using Management.Core.AdminRole;
using Management.Core.Permission;
using Management.Core.RolePermission;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Management.Areas.Control
{
    /// <summary>
    /// 管理员身份验证类
    /// </summary>
    public class AdminAuthorizeAttribute : AuthorizeAttribute
    {
        private AdminRoleManager adminRoleManager = new AdminRoleManager();
        private RolePermissionManager rolePermissionManager = new RolePermissionManager();
        private PermissionManager permissionManager = new PermissionManager();
        readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        /// <summary>
        /// 重写自定义授权检查
        /// </summary>
        /// <returns></returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext.Session["AdminID"] != null && RedisHelper.Get<string>(httpContext.Session["AdminID"].ToString()) != null)
            {
                string oldSessionid = RedisHelper.Get<string>(httpContext.Session["AdminID"].ToString());
                if (httpContext.Session.SessionID != oldSessionid)
                {
                    httpContext.Session.Clear();
                    HttpCookie cookie = new HttpCookie("ForcedOff");
                    cookie.Value = httpContext.Request.UserHostAddress;
                    cookie.Expires.AddSeconds(30);
                    HttpContext.Current.Response.AppendCookie(cookie);
                }
            }
            GetCA(httpContext);
            if (httpContext.Session["AdminID"] == null)
            {   
                return false;
            }            
            else
            {
                //return true;
                int adminid = System.Convert.ToInt32(httpContext.Session["AdminID"]);
                if (hasPermission(adminid, httpContext))
                    return true;
                else
                    return false;
            }
        }
        /// <summary>
        /// 重写未授权的 HTTP 请求处理
        /// </summary>
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectResult("~/Control/Admin/Login");
            HttpCookie cookie = new HttpCookie("unauthorized");
            cookie.Value = "unauthorized";
            cookie.Expires.AddSeconds(30);
            HttpContext.Current.Response.AppendCookie(cookie);
        }

        //权限控制(在控制器里通过类似于切面编程的方式进行调用该过滤器时会根据该标志结果来判断用户权限)
        private bool hasPermission(int adminid, HttpContextBase httpContext)
        {
            if (RedisHelper.Get<List<int>>("permissionids") == null)
            {
                return false;
            }
            string url = httpContext.Request.Path;
            string[] _params = url.Split('/');
            var _controller = _params[2];
            var _action = _params.Length > 3 ? _params[3] : "Index";
            System.Text.StringBuilder sb = new System.Text.StringBuilder();            
            //找到正在访问的界面需要的权限
            if (permissionManager.Count(u => u.Controller == _controller.ToString() && u.Action == _action.ToString()) > 0)
            {
                int RequiredPermissionid = permissionManager.Find(u => u.Controller == _controller.ToString() && u.Action == _action.ToString()).PermissionID;                
                List<int> hasPermissionids = RedisHelper.Get<List<int>>("permissionids");
                if (hasPermissionids.Contains(RequiredPermissionid))
                {
                    logger.Info("用户" + httpContext.Session["Accounts"].ToString() + "(id:" + adminid.ToString() + ",ip:"+ httpContext.Request.UserHostAddress +")访问了" + _controller + "控制器下的" + _action + "动作。");
                    return true;
                }
                else
                {
                    logger.Warn("用户" + httpContext.Session["Accounts"].ToString() + "(id:" + adminid.ToString() + ",ip:" + httpContext.Request.UserHostAddress + ")试图访问" + _controller + "控制器下的" + _action + "动作，因没有权限被拒绝！");
                    return false;
                }
            }
            else
            {
                logger.Warn("用户" + httpContext.Session["Accounts"].ToString() + "(id:" + adminid.ToString() + ")试图访问" + _controller + "控制器下的" + _action + "动作，因没有权限被拒绝！");
                return false;
            }
        }

        private void GetCA(HttpContextBase httpContext)
        {
            
            string url = httpContext.Request.Path;
            string[] _params = url.Split('/');
            var _controller = _params[2];
            var _action = _params.Length > 3 ? _params[3] : "Index";

            //小试一下redis缓存            
            if (RedisHelper.Get<List<Permission>>("permissionList") == null || RedisHelper.Get<List<Permission>>("permissionList").Count() == 0)
            {
                RedisHelper.Set<List<Permission>>("permissionList", permissionManager.FindList().ToList(), DateTime.Now.AddHours(1) - DateTime.Now);
            }
            var permissionList = RedisHelper.Get<List<Permission>>("permissionList");
            bool flag = false;
            foreach(var permission in permissionList)
            {
                if(permission.Controller == _controller && permission.Action == _action)
                {
                    flag = true;
                    break;                    
                }
                else
                {
                    continue;
                }
            }
            if (!flag)
            {
                Permission permission = new Permission();
                permission.Controller = _controller.ToString();
                permission.Action = _action.ToString();
                permission.Cteated_at = DateTime.Now;
                permission.Updated_at = DateTime.Now;
                permissionManager.Add(permission);
                RedisHelper.DelKey("permissionList");
                RedisHelper.DelKey("controller" + _controller);
            }
        }
    }

    /// <summary>
    /// redis帮助类
    /// </summary>
    public class RedisHelper
    {
        private static string _conn = ConfigurationManager.AppSettings["Redis"] ?? "127.0.0.1:6379";
        public static string StringGet(string key)
        {
            try
            {
                using (var client = ConnectionMultiplexer.Connect(_conn))
                {
                    return client.GetDatabase().StringGet(key);
                }
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static string[] StringGetMany(string[] keyStrs)
        {
            var count = keyStrs.Length;
            var keys = new RedisKey[count];
            var addrs = new string[count];
            for (var i = 0; i < count; i++)
            {
                keys[i] = keyStrs[i];
            }
            try
            {
                using (var client = ConnectionMultiplexer.Connect(_conn))
                {
                    var values = client.GetDatabase().StringGet(keys);
                    for (var i = 0; i < values.Length; i++)
                    {
                        addrs[i] = values[i];
                    }
                    return addrs;
                }
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// 单条存值
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="value">The value.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool StringSet(string key, string value)
        {
            using (var client = ConnectionMultiplexer.Connect(_conn))
            {
                return client.GetDatabase().StringSet(key, value);
            }
        }

        /// <summary>
        /// 批量存值
        /// </summary>
        /// <param name="keysStr">key</param>
        /// <param name="valuesStr">The value.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool StringSetMany(string[] keysStr, string[] valuesStr)
        {
            var count = keysStr.Length;
            var keyValuePair = new KeyValuePair<RedisKey, RedisValue>[count];
            for (int i = 0; i < count; i++)
            {
                keyValuePair[i] = new KeyValuePair<RedisKey, RedisValue>(keysStr[i], valuesStr[i]);
            }
            using (var client = ConnectionMultiplexer.Connect(_conn))
            {
                return client.GetDatabase().StringSet(keyValuePair);
            }
        }

        public static bool DelKey(string key)
        {
            using (var client = ConnectionMultiplexer.Connect(_conn))
            {
                return client.GetDatabase().KeyDelete(key);
            }
        }

        #region 泛型
        /// <summary>
        /// 存值并设置过期时间
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">key</param>
        /// <param name="t">实体</param>
        /// <param name="ts">过期时间间隔</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool Set<T>(string key, T t, TimeSpan ts)
        {
            var str = JsonConvert.SerializeObject(t);
            using (var client = ConnectionMultiplexer.Connect(_conn))
            {
                return client.GetDatabase().StringSet(key, str, ts);
            }
        }

        /// <summary>
        /// 
        /// 根据Key获取值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The key.</param>
        /// <returns>T.</returns>
        public static T Get<T>(string key) where T : class
        {
            using (var client = ConnectionMultiplexer.Connect(_conn))
            {
                var strValue = client.GetDatabase().StringGet(key);
                return string.IsNullOrEmpty(strValue) ? null : JsonConvert.DeserializeObject<T>(strValue);
            }
        }
        #endregion
    }
}